<script setup>
import {ref, onMounted, reactive, onBeforeUnmount} from 'vue'
import {
  CAROUSEL_IMAGE_DISPLAY_MODE_ATTACHMENT_FIXED,
  CAROUSEL_IMAGE_DISPLAY_MODE_CONTAIN,
  CAROUSEL_IMAGE_DISPLAY_MODE_COVER
} from "@/constant/tail_carousel_constant.js";

/**
 * 接收父组件传递的参数
 * @type {Prettify<Readonly<ExtractPropTypes<{}>>>}
 */
const props = defineProps({
  // 图片路径
  imagesUrl: {type: Array, default: []},
  // 图片轮播间隔时间，单位 ms
  carouselInterval: {type: Number, default: 3000},
  // 轮播图的宽
  carouselWidth: {type: String, default: '50vw'},
  // 轮播图的高
  carouselHeight: {type: String, default: '50vh'},
  // 图片的显示模式
  imageDisplayMode: {type: String, default: 'contain'},
  // 是否在鼠标悬浮与图片指示器上时切换图片
  changeImgByMouseover: {type: Boolean, default: false},
  // 是否在鼠标点击图片指示器时切换图片
  changeImgByClick: {type: Boolean, default: false},
  // 是否开启底部过渡遮罩
  enableBottomGradientCover: {type: Boolean, default: false},
  // 底部过渡遮罩高度，单位 rem
  bottomGradientCoverHeight: {type: Number, default: 3},
  // 底部过度遮罩颜色
  bottomGradientCoverColor: {type: String, default: 'var(--tail-carousel-bottom-gradient-cover-color)'},
})

/**
 * 动态生成轮播图的样式
 */
const carouselStyle = reactive({
  width: props?.carouselWidth,
  height: props?.carouselHeight,
})

/**
 * 动态生成图片的样式
 */
const imageStyle = reactive({
  backgroundSize: '',
  backgroundAttachment: '',
  transform: '',
  transition: ''
})

// 根据指定的图片的显示模式设置样式
function setImageStyleByImageDisplayMode() {
  switch (props?.imageDisplayMode) {
    case CAROUSEL_IMAGE_DISPLAY_MODE_COVER:
      imageStyle.backgroundSize = CAROUSEL_IMAGE_DISPLAY_MODE_COVER
      imageStyle.backgroundAttachment = 'unset'
      break
    case CAROUSEL_IMAGE_DISPLAY_MODE_CONTAIN:
      imageStyle.backgroundSize = CAROUSEL_IMAGE_DISPLAY_MODE_CONTAIN
      imageStyle.backgroundAttachment = 'unset'
      break
    case CAROUSEL_IMAGE_DISPLAY_MODE_ATTACHMENT_FIXED:
      imageStyle.backgroundSize = 'unset'
      imageStyle.backgroundAttachment = CAROUSEL_IMAGE_DISPLAY_MODE_ATTACHMENT_FIXED
      break;
  }
}

// 当图片的个数大于 1
if (props?.imagesUrl.length > 1) {
  // 根据指定的图片的显示模式设置样式
  setImageStyleByImageDisplayMode()
}
// 当图片的个数等于 1
else if (props?.imagesUrl.length === 1) {
  imageStyle.transform = 'translateX(0)'
  imageStyle.transition = 'unset'
  // 根据指定的图片的显示模式设置样式
  setImageStyleByImageDisplayMode()
}

/**
 * 获取轮播图中的所有放置图片的元素
 * @type {Ref<UnwrapRef<*[]>>}
 */
const carouselImageElsRef = ref([])
/**
 * 为轮播图中的所有放置图片的元素设置图片地址
 */
onMounted(() => {
  // 当图片的个数大于 1
  if (props?.imagesUrl.length > 1) {
    carouselImageElsRef.value.forEach((carouselImageEl, idx) => {
      // 轮播的图片最后会再加一个第一张图片，用以实现无缝轮播
      if (idx >= props?.imagesUrl.length + 1) {
        carouselImageEl.style.backgroundImage = `url(${props?.imagesUrl[0]})`
      } else if (idx === 0) {
        carouselImageEl.style.backgroundImage = `url(${props?.imagesUrl[props?.imagesUrl.length - 1]})`
      } else {
        carouselImageEl.style.backgroundImage = `url(${props?.imagesUrl[idx - 1]})`
      }
    })
  }
  // 当图片的个数等于 1
  else if (props?.imagesUrl.length === 1) {
    // 获取图片元素
    const carouselImageEl = document.querySelector('.tail-carousel .tail-carousel-images .tail-carousel-image')
    // 设置图片
    carouselImageEl.style.backgroundImage = `url(${props?.imagesUrl[0]})`
  }
})

/**
 * 实现轮播效果
 */
/**
 * 为了实现无缝轮播，在第一张图片前添加了一个“假最后一张”图片，
 * 在最后一张图片后添加了一个“假第一张”图片，所以 v-for 遍历
 * 的值应当为图片总个数 + 2，idx=0 为“假最后一张”图片，
 * idx=imagesUrl.length+1 为“假第一张”图片，idx=1 为第
 * 一张图片，idx=imagesUrl.length 为“真最后一张”图片
 */
// 动态记录当前展示的图片的序号
const nowIdx = ref(1)
// 保存定时器
let timeout = null

// 切换下一张图片
function nextImage() {
  // 添加过渡效果，每次移动图片前添加，以可以实现从“假第一张”图片切换至“真”第一张图片时无过渡效果
  imageStyle.transition = 'all 0.5s'
  // 移动图片
  nowIdx.value++
  imageStyle.transform = `translateX(-${100 * nowIdx.value}%)`
  // 过渡到“假”第一张图片后，添加与过渡效果时长相同的定时器执行切换至“真”第一张图片
  if (nowIdx.value >= props?.imagesUrl.length + 1) {
    timeout = setTimeout(() => {
      // 清除之前的定时器
      clearTimeout(timeout)
      nowIdx.value = 1
      // 清除过渡效果
      imageStyle.transition = 'none'
      // 切换至“真”第一张图片
      imageStyle.transform = `translateX(-100%)`
    }, 500);
  }
}

// 切换上一张图片
function preImage() {
  // 添加过渡效果，每次移动图片前添加，以可以实现从“假最后一张”图片切换至“真”最后一张图片时无过渡效果
  imageStyle.transition = 'all 0.5s'
  // 移动图片
  nowIdx.value--
  imageStyle.transform = `translateX(-${100 * nowIdx.value}%)`
  // 过渡到“假”最后一张图片后，添加与过渡效果时长相同的定时器执行切换至“真”最后一张图片
  if (nowIdx.value <= 0) {
    timeout = setTimeout(() => {
      // 清除之前的定时器
      clearTimeout(timeout)
      nowIdx.value = props?.imagesUrl.length
      // 清除过渡效果
      imageStyle.transition = 'none'
      // 切换至“真”最后一张图片
      imageStyle.transform = `translateX(-${100 * props?.imagesUrl.length}%)`
    }, 500)
  }
}

/**
 * 实现自动轮播
 */
// 保存定时器
let interval = null

// 创建自动轮播定时器
function createAutoCarouselInterval(ms) {
  // 当图片的个数大于 1 才创建定时器（html 中也绑定了事件所以在这进行控制）
  if (props?.imagesUrl.length > 1) {
    // 创建定时器
    interval = setInterval(() => {
      nextImage()
    }, ms)
  }
}

// 组件挂载之后创建自动轮播定时器
onMounted(() => {
  // 创建自动轮播定时器
  createAutoCarouselInterval(props?.carouselInterval)
})

// 删除自动轮播定时器
function removeAutoCarouselInterval() {
  // 当图片的个数大于 1 才创建定时器（html 中也绑定了事件所以在这进行控制）
  // if (props?.imagesUrl.length > 1) {
  clearInterval(interval)
  // }
}

// 组件卸载之前删除自动轮播定时器
onBeforeUnmount(() => {
  removeAutoCarouselInterval()
})

/**
 * 轮播图鼠标悬浮事件处理器函数
 */
// 是否显示切换图片按钮
const changeImageBtnIsShow = ref(false)
// 是否显示图片指示器
const imageIndicatorIsShow = ref(false)

// 鼠标在轮播图上移动
function mouseoverHandler() {
  // 显示切换图片按钮
  changeImageBtnIsShow.value = true
  // 显示图片指示器
  imageIndicatorIsShow.value = true
  // 移除自动轮播定时器
  removeAutoCarouselInterval()
}

// 鼠标移除轮播图
function mouseleaveHandler() {
  // 隐藏切换图片按钮
  changeImageBtnIsShow.value = false
  // 隐藏图片指示器
  imageIndicatorIsShow.value = false
  // 创建自动轮播定时器
  createAutoCarouselInterval(props?.carouselInterval)
}

/**
 * 切换到指定的图片
 */
// 切换到指定的图片函数，第一张图片索引为 1
function changeImageTo(imgIdx) {
  // 越界直接退出
  if (imgIdx < 1 || imgIdx > props?.imagesUrl.length) return
  // 添加过渡效果
  imageStyle.transition = 'all 0.5s'
  // 移动图片
  nowIdx.value = imgIdx
  imageStyle.transform = `translateX(-${100 * nowIdx.value}%)`
}
// 图片指示器鼠标悬浮处理器
function imageIndicatorMouseoverHandler(imgIdx) {
  // 在鼠标悬浮与图片指示器上时切换图片
  if (props?.changeImgByMouseover) {
    changeImageTo(imgIdx)
  }
}
// 图片指示器鼠标点击处理器
function imageIndicatorClickHandler(imgIdx) {
  // 在鼠标点击图片指示器时切换图片
  if (props?.changeImgByClick) {
    changeImageTo(imgIdx)
  }
}

/**
 * 图片指示器激活样式类名处理
 */
function imageIndicatorActive(imgIdx) {
  const imagesUrlLen = props?.imagesUrl.length
  // 第一张图片特殊处理
  if (imgIdx === 1 && (nowIdx.value === 0 || nowIdx.value === 1)) {
    return 'active'
  }
  // 最后一张图片特殊处理
  else if (imgIdx === imagesUrlLen && (nowIdx.value === imagesUrlLen || nowIdx.value === imagesUrlLen + 1)) {
    return 'active'
  }
  // 索引相同
  else if (imgIdx === nowIdx.value) {
    return 'active'
  }
  return ''
}

/**
 * 动态设置底部过渡遮罩样式
 */
const bottomGradientCoverEleStyle = reactive({
  height: `${props?.bottomGradientCoverHeight}rem`,
  backgroundColor: props?.bottomGradientCoverColor,
  // 阴影颜色 水平阴影位置 垂直阴影位置 模糊距离 阴影大小 内阴影
  boxShadow: `${props?.bottomGradientCoverColor} 0 -${props?.bottomGradientCoverHeight * 1.5}rem ${props?.bottomGradientCoverHeight * 4}rem ${props?.bottomGradientCoverHeight * 5}rem`
})
</script>

<template>
  <!-- 小尾巴轮播图组件 -->
  <div
      class="tail-carousel"
      :style="carouselStyle"
      @mouseover="mouseoverHandler"
      @mouseleave="mouseleaveHandler"
  >

    <!-- 小尾巴轮播图组件图片 -->
    <div class="tail-carousel-images">
      <!-- 图片只有一个时 -->
      <div
          v-if="imagesUrl.length === 1"
          class="tail-carousel-image"
          :style="imageStyle"
      ></div>
      <!-- 小尾巴轮播图组件的每个图片 -->
      <template
          v-else-if="imagesUrl.length > 1"
      >
        <div
            class="tail-carousel-image"
            :style="imageStyle"
            v-for="idx in (imagesUrl.length + 2)"
            :key="idx"
            ref="carouselImageElsRef"
        ></div>
        <!-- 切换到上一张图片 -->
        <div
            v-show="changeImageBtnIsShow"
            class="tail-carousel-pre-image-btn"
            @click="preImage"
        >
          <slot name="carouselPreImageBtn"></slot>
        </div>
        <!-- 切换到下一张图片 -->
        <div
            v-show="changeImageBtnIsShow"
            class="tail-carousel-next-image-btn"
            @click="nextImage"
        >
          <slot name="carouselNextImageBtn"></slot>
        </div>
        <!-- 图片指示器 -->
        <div
            v-show="imageIndicatorIsShow"
            class="tail-carousel-image-indicator"
        >
          <span
              class="tail-carousel-image-indicator-item"
              v-for="idx in imagesUrl.length"
              :key="idx"
              @click="imageIndicatorClickHandler(idx)"
              @mouseover="imageIndicatorMouseoverHandler(idx)"
          >
            <span :class="imageIndicatorActive(idx)"></span>
          </span>
        </div>
      </template>
    </div>
    <!-- 图片底部过渡遮罩 -->
    <div
        v-if="enableBottomGradientCover"
        class="tail-carousel-bottom-gradient-cover"
        :style="bottomGradientCoverEleStyle"
    ></div>
  </div>
</template>

<style scoped lang="scss">
/*
 * 小尾巴轮播图组件
 */
.tail-carousel {
  position: relative;
  background-color: var(--tail-carousel-bgc);
  overflow: hidden;
  color: var(--tail-carousel-font-color);

  /*
   * 小尾巴轮播图组件图片
   */
  .tail-carousel-images {
    width: 100%;
    height: 100%;
    // flex 且子元素不压缩，隐藏超出父元素的子元素
    //overflow: hidden;
    display: flex;
    justify-content: flex-start;
    align-items: center;

    /*
     * 小尾巴轮播图组件的每个图片
     */
    .tail-carousel-image {
      // 初始状态显示真第一张图片
      transform: translateX(-100%);
      // 弹性盒子中该元素不会被压缩
      flex-shrink: 0;
      box-sizing: border-box;
      // 为父元素的百分百宽高
      width: 100%;
      height: 100%;
      background-repeat: no-repeat;
      background-position: center;
      transition: all 0.5s;
    }

    /*
     * 切换到上一张图片和切换到下一张图片公共样式
     */
    .tail-carousel-pre-image-btn,
    .tail-carousel-next-image-btn {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 2rem;
      height: 2rem;
      border-radius: 50%;
      background-color: var(--tail-carousel-change-image-btn-bgc);
      cursor: pointer;
      transition: all 0.3s;
      display: flex;
      justify-content: center;
      align-items: center;
      color: var(--tail-carousel-change-image-btn-font-color);
      user-select: none;

      &:hover {
        background-color: var(--tail-carousel-change-image-btn-hover-bgc);
        color: var(--tail-carousel-change-image-btn-hover-font-color);
      }
    }

    /*
     * 切换到上一张图片
     */
    .tail-carousel-pre-image-btn {
      left: 1rem;
    }

    /*
     * 切换到下一张图片
     */
    .tail-carousel-next-image-btn {
      right: 1rem;
    }

    /*
     * 图片指示器
     */
    .tail-carousel-image-indicator {
      position: absolute;
      left: 50%;
      bottom: 0.5rem;
      transform: translateX(-50%);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;

      .tail-carousel-image-indicator-item {
        margin: 0 0.2rem;
        padding: 0.2rem 0;
        border-radius: 0.1rem;

        &:hover {

          span {
            background-color: var(--tail-carousel-change-image-btn-hover-bgc);
          }
        }

        span {
          display: inline-block;
          width: 1.5rem;
          height: 0.2rem;
          background-color: var(--tail-carousel-change-image-btn-bgc);
        }

        .active {
          background-color: var(--tail-carousel-change-image-btn-active-bgc);
        }
      }
    }
  }

  /*
   * 图片底部过渡遮罩
   */
  .tail-carousel-bottom-gradient-cover {
    position: absolute;
    left: 0;
    bottom: 0;
    z-index: 1;
    width: 100%;
    pointer-events: none; // 取消本层次样式的事件
  }
}
</style>